package util

import (
	"bytes"
	"fmt"
	"github.com/gammazero/deque"
	"golang.org/x/net/html"
	"golang.org/x/net/html/atom"
	"reflect"
	"strconv"
)

func GetAttr(node *html.Node, key string) (string, bool) {
	for _, attr := range node.Attr {
		if key == attr.Key {
			return attr.Val, true
		}
	}
	return "", false
}

func HasAttr(node *html.Node, key string) bool {
	_, ok := GetAttr(node, key)
	return ok
}

func FindChild(node *html.Node, at atom.Atom) *html.Node {
	if node.FirstChild == nil {
		return nil
	}
	q := deque.Deque[*html.Node]{}
	q.PushBack(node.FirstChild)
	for q.Len() > 0 {
		n := q.PopFront()
		if n.DataAtom == at {
			return n
		}
		if n.FirstChild != nil {
			q.PushBack(n.FirstChild)
		}
		if n.NextSibling != nil {
			q.PushBack(n.NextSibling)
		}
	}
	return nil
}

func FindChildren(node *html.Node, at atom.Atom) []*html.Node {
	if node.FirstChild == nil {
		return nil
	}
	nodes := make([]*html.Node, 0)
	q := deque.Deque[*html.Node]{}
	q.PushBack(node.FirstChild)
	for q.Len() > 0 {
		n := q.PopFront()
		if n.DataAtom == at {
			nodes = append(nodes, n)
		}
		if n.FirstChild != nil {
			q.PushBack(n.FirstChild)
		}
		if n.NextSibling != nil {
			q.PushBack(n.NextSibling)
		}
	}
	return nodes
}

func ToList(val any) []interface{} {
	if val == nil {
		return nil
	}
	switch reflect.TypeOf(val).Kind() {
	case reflect.Slice, reflect.Array:
		data := make([]interface{}, 0)
		s := reflect.ValueOf(val)
		for i := 0; i < s.Len(); i++ {
			data = append(data, s.Index(i).Interface())
		}
		return data
	}
	return nil
}

func ToMap(val any) map[string]interface{} {
	if m, ok := val.(map[string]interface{}); ok {
		return m
	}
	return map[string]interface{}{}
}

func CloneMap(m map[string]interface{}) map[string]interface{} {
	data := map[string]interface{}{}
	for k, v := range m {
		data[k] = v
	}
	return data
}

func ToString(val any) string {
	return fmt.Sprintf("%v", val)
}

func ToFloat(val any) float64 {
	v, _ := strconv.ParseFloat(ToString(val), 64)
	return v
}

func CloneNode(n *html.Node) *html.Node {
	m := &html.Node{
		Type:     n.Type,
		DataAtom: n.DataAtom,
		Data:     n.Data,
		Attr:     make([]html.Attribute, len(n.Attr)),
	}
	if n.FirstChild != nil {
		next := n.FirstChild
		for next != nil {
			m.AppendChild(CloneNode(next))
			next = next.NextSibling
		}
	}
	copy(m.Attr, n.Attr)
	return m
}

func BuildKey(values ...string) string {
	buf := bytes.Buffer{}
	for _, v := range values {
		buf.WriteString(v)
		buf.WriteByte('_')
	}
	return buf.String()
}

func GetContent(n *html.Node) string {
	if n.FirstChild == nil {
		return ""
	}
	return n.FirstChild.Data
}

func SetContent(n *html.Node, data string) {
	if n.FirstChild == nil {
		n.AppendChild(&html.Node{
			Type: html.TextNode,
			Data: data,
		})
		return
	}
	n.FirstChild.Data = data
}
